import React, { forwardRef, Ref, useEffect, useImperativeHandle, useRef, useState } from "react";
import './XTable.css'
import TextEditor from "@/demos/editors/textEditor";
import dateEditor from "@/demos/editors/dateEditor";
import DataGridComboBoxEditor from "@/demos/editors/dataGridComboBoxEditor";
import { XTableProps, XTableColumn } from "@/demos/types";
import DateEditor from "@/demos/editors/dateEditor";
import dayjs from "dayjs";


const XTable = forwardRef((
    {
        columns, data, selectedIdx = -1, onSelectChange, selectMode = 'row', selectable = false,
        defaultSelections = [],
        onSelectionsChange,
        onCommit,
    }: XTableProps,
    ref) => {

    // 当组件挂载时，将内部 ref 暴露给父组件
    useImperativeHandle(ref, () => ({
        scrollToIdx,
        focus: () => {
            dataGridRef.current && dataGridRef.current.focus();
        },
        setPreSelectIdx,
        select: (idx: number) => {
            setCurrentRowIdx(idx)
            setPreSelectIdx(idx)
            scrollToIdx(idx)
        },
        setCurrentRowIdx: (idx: number) => {
            setCurrentRowIdx(idx)
            setPreSelectIdx(idx)
        },
        clearSelection: () => {
            setCurrentRowIdx(-1)
            setPreSelectIdx(-1)
        }
    }));

    const [currentRowIdx, setCurrentRowIdx] = useState(-1);
    const [preSelectIdx, setPreSelectIdx] = useState(-1)
    const dataGridRef = useRef<HTMLTableElement>(null);
    const checkAllRef = useRef<HTMLInputElement>(null);
    const [position, setPosition] = useState([-1, -1])
    const [isEditing, setIsEditing] = useState(false)
    const [selections, setSelections] = useState(defaultSelections)
    const [dataRows, setDataRows] = useState(data)
    useEffect(() => {
        setDataRows(data)
    }, [data]);
    const scrollToIdx = (idx: number) => {
        if (dataGridRef.current && !isEditing) {
            dataGridRef.current.scrollTo({
                top: idx * 12,
                behavior: 'smooth'
            })
        }
    }

    function getRowClassName(row: any, idx: number) {
        return (isSelect(idx) ? `selected-row` : (preSelectIdx === idx ? `pre-selected-row` : ``)) + ' gridrow'
    }

    const handleOptionClick = (e: React.MouseEvent, rid: any | null, cid: number) => {
        if (isEditing) {
            if (position[0] != rid || position[1] != cid) {
                endEdit()
                selectCell([rid, cid])
            }
            return;
        }

        if (selectMode == 'row') {
            setCurrentRowIdx(rid);
            setPreSelectIdx(rid);
            onSelectChange?.(dataRows[rid], rid)
            e.stopPropagation()
        }

        if (selectMode === 'column') {
            if (position[0] == rid && position[1] == cid && columns[cid].editor) {
                startEdit()
            } else {
                selectCell([rid, cid])
            }
        }
        e.stopPropagation()
    };

    const preSelectRow = (rowIdx: number) => {
        dataRows.map((row, idx) => {
            if (idx === rowIdx) {
                setPreSelectIdx(idx)
                scrollToIdx(idx)
            }
        })
    }

    const selectCell = (pos: number[]) => {
        setPosition(pos)
        setPreSelectIdx(pos[0])
        onSelectChange && onSelectChange(dataRows[pos[0]], pos[0])
    }

    const onInputKeyDown = (event: React.KeyboardEvent<any>) => {
        if (event.code == 'ArrowDown' && !isEditing) {
            if (selectMode == 'row') {
                if (preSelectIdx < dataRows.length) {
                    preSelectRow(preSelectIdx + 1)
                }
            } else {
                if (position[0] + 1 < dataRows.length) {
                    selectCell([position[0] + 1, position[1]])
                }
            }
        }
        if (event.code == 'ArrowUp' && !isEditing) {
            if (selectMode == 'row') {
                if (preSelectIdx > 0) {
                    preSelectRow(preSelectIdx - 1)
                }
            } else {
                if (position[0] > 0) {
                    selectCell([position[0] - 1, position[1]])
                }
            }
        }

        if (event.code == 'ArrowLeft' && !isEditing) {
            if (position[1] > 0) {
                selectCell([position[0], position[1] - 1])
            }
        }

        if (event.code == 'ArrowRight' && !isEditing) {
            if (position[1] + 1 < columns.length) {
                selectCell([position[0], position[1] + 1])
            }
        }
        if (event.code == 'Enter') {
            if (selectMode === 'row') {
                console.log('on table selection change')
                onSelectChange && onSelectChange(dataRows[preSelectIdx], preSelectIdx)
                event.stopPropagation()
            }
            toggleEdit()
        }
    }
    const toggleEdit = () => {
        if (selectMode == 'row') {
            setCurrentRowIdx(preSelectIdx)
            setPreSelectIdx(preSelectIdx)
        } else {
            if (isEditing) {
                endEdit()
            } else {
                // 开启编辑模式
                startEdit()
            }
        }
    }

    const toNextCell = () => {
        if (position[1] + 1 < columns.length) {
            selectCell([position[0], position[1] + 1])
        } else {
            if (position[0] + 1 < dataRows.length) {
                selectCell([position[0] + 1, 0])
            } else {
                console.log('已经到底了~')
                addRow()
            }
        }
    }

    const addRow = () => {
        console.log('添加一行')
        const newRow: any = {}
        columns.map(col => {
            newRow[col.prop] = ""
        })
    }

    const endEdit = () => {
        setIsEditing(false)
        dataGridRef.current?.focus()
        onCommit && onCommit(dataRows[currentRowIdx], columns[position[1]], position[0], position[1])
    }
    const startEdit = () => {
        const currentCell = columns[position[1]];
        if (currentCell.editor) {
            setIsEditing(true)
        } else {
            toNextCell()
        }
    }

    const getCellClassName = (rid: number, cid: number) => {
        return (position[0] == rid && position[1] == cid ?
            'selected-cell' : (columns[cid].editor ? 'editable-cell' : '')) + ' td-container';
    }

    const tabIndex = -1
    const afterCommit = (row: any, rowIdx: number, column: XTableColumn, val: any) => {
        row[column.prop] = val
        endEdit()
        toNextCell()
    }
    const getEditor = (column: XTableColumn, row: any, rowIdx: number, colIdx: number) => {
        switch (column.editor) {
            case 'dataGridComboBoxEditor':
                return column.settings && (
                    <DataGridComboBoxEditor
                        columns={column.settings.columns}
                        dataSource={column.settings.dataSource}
                        displayField={column.settings.displayField}
                        valueField={column.settings.valueField}
                        defaultText={row[column.prop]}
                        value={row[column.settings.valueField]}
                        onCommit={(selRow, selIdx, selText) => {
                            console.log('DataGridComboBoxEditor commit:',column, selRow, selIdx, selText)
                            setDataRows(predata => {
                                console.log('set data rows', column.settings)
                                if (column.settings) {
                                    predata[rowIdx][column.settings.valueField] =
                                        selRow[column.settings.valueField];
                                    if(column.settings.onCommit){
                                        console.log('column onCommit setted!')
                                        column.settings.onCommit(rowIdx, column, {selRow, selIdx, selText})
                                    }
                                }
                                return predata;
                            })
                            afterCommit(row, rowIdx, column, selText)
                        }}
                    />
                )
            case 'textEditor':
                return (
                    <TextEditor defaultValue={row[column.prop]} onCommit={(val) => {
                        afterCommit(row, rowIdx, column, val)
                    }} />
                )
            case 'dateEditor':
                const getToday = (d: string) => {
                    var today = dayjs(d).format('YYYY-MM-DD');
                    console.log(today)
                    return today;
                }
                var td = getToday(row[column.prop])
                return (
                    <DateEditor defaultValue={td} onCommit={(val) => {
                        console.log('text editor on commit => ', val)
                        afterCommit(row, rowIdx, column, val)
                    }} />
                )
            default:
                return (
                    <></>
                )
        }
    }

    const handleDbClick = (e: React.MouseEvent, rowIdx: number, colIdx: number) => {
        if (columns[colIdx].editor) {
            selectCell([rowIdx, colIdx])
            toggleEdit()
        }
        e.stopPropagation()
    }

    const getTd = (row: any, column: XTableColumn, rowIdx: number, colIdx: number) => {
        if (isEditing && position[0] == rowIdx && position[1] == colIdx) {
            return (
                <div key={colIdx} style={{ "width": column.width + 'px' }} className="td-container">
                    {getEditor(column, row, rowIdx, colIdx)}
                </div>
            )
        } else {
            return (
                <div onDoubleClick={(e) => handleDbClick(e, rowIdx, colIdx)}
                    onClick={(e) => handleOptionClick(e, rowIdx, colIdx)}
                    className={getCellClassName(rowIdx, colIdx)}
                    style={{ "width": column.width + 'px' }}
                    key={colIdx}>

                    <div className="td-text">
                        {column.formatter ? column.formatter(row[column.prop], row) : row[column.prop]}
                    </div>
                </div>
            )
        }
    }

    const checkAll = (e: React.BaseSyntheticEvent) => {
        if (e.target.checked) {
            setSelections(Array.from(Array(dataRows.length).keys()))
        } else {
            setSelections([])
        }
        emitOnSelectionsChange()
    }

    const checkRow = (e: React.BaseSyntheticEvent, row: any, rowIdx: number) => {
        if (e.target.checked) {
            if (!isSelect(rowIdx)) {
                setSelections([...selections, ...[rowIdx]])
            }
        } else {
            setSelections(selections.filter(item => item != rowIdx))
        }
        emitOnSelectionsChange()
    }

    const emitOnSelectionsChange = () => {
        onSelectionsChange && onSelectionsChange(selections)
    }

    const isSelect = (rowIdx: number) => {
        return selections.some(item => item == rowIdx)
    }

    return (
        <div className="data-grid-container" onKeyDown={onInputKeyDown}>
            <div className="x-table">
                <div className="gridrow table-header">
                    {selectable && (
                        <div className="checkbox-cell">
                            <input ref={checkAllRef} type="checkbox"
                                checked={dataRows.length == selections.length}
                                readOnly={true}
                                onClick={e => checkAll(e)} />
                        </div>
                    )}
                    {columns.map((column, idx) => (
                        <div key={idx} style={{ "width": column.width + 'px' }} className="td-container">
                            <div className="td-text">
                                {column.label}
                            </div>
                        </div>
                    ))}
                    <div className="td-container last-cell"></div>
                </div>
            </div>


            <div className="data-grid">
                <div className="x-table" tabIndex={tabIndex} ref={dataGridRef}>
                    {data.map((row, rowIdx) => {
                        return (
                            <div className={getRowClassName(row, rowIdx)} key={rowIdx}>
                                {selectable && (
                                    <div className="checkbox-cell">
                                        <input type="checkbox" onClick={e =>
                                            checkRow(e, row, rowIdx)}
                                            checked={isSelect(rowIdx)} readOnly={true} />
                                    </div>
                                )}
                                {columns.map((column, colIdx) => (
                                    getTd(row, column, rowIdx, colIdx)
                                ))}
                                <div className="td-container last-cell"></div>
                            </div>
                        )

                    })}
                </div>
            </div>
        </div>
    )
})

export default XTable
